The backend for Real Cooperative in the African Diaspora. This is a NodeJS server built to handle incoming requests to communicate with a SurrealDB database.
- Flexibility
- Scalability
- Reliability
- Efficiency
- Security
- npm
- SurrealDB
curl --proto '=https' --tlsv1.2 -sSf https://install.surrealdb.com | sh
iwr https://windows.surrealdb.com -useb | iex
Start SurrealDB in memory and define the user scope and table
surreal start --log debug --user root --pass root memory &
# runs an import of the user schema and scope if you aren't running a terminal in the cloned repo change the path of user.surql
surreal import --conn http://localhost:8000 --user root --pass root --ns test --db test user.surql
Variable | Description | Default |
---|---|---|
SURREAL_DB | SurrealDB connection string | http://localhost:8000/rpc |
SERVER_URL | Server URL (used for files) | http://localhost:4000 |
Either create a .env file and set the variables or run this command in the terminal
cat << EOF > .env
SURREAL_DB="http://localhost:8000/rpc"
SERVER_URL="http://localhost:4000"
EOF
npm install
npm run dev
You can find usage examples of every route in test.rest.
(in progress)
To sign up send a POST request to /signup
. The body of the request should be a JSON object with a username
and password
property. You can add additional properties as well, but they'll need to be defined in user.surql or through a SurrealQL request directily to the database. The /signup
endpoint will return a JWT token that can be used to authenticate the user.
// JavaScript usage
const body = {
username: "notSpiderman",
password: "muhGuire",
email: "[email protected]",
settings: {
marketing: true,
},
};
const url = "http://localhost:4000/api/v1/signup";
let response = await fetch(url, {
method: "POST",
body: JSON.stringify(body),
});
let data = await response.json();
console.log(data);
Response:
{
"status": "OK",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE2ODkwNDA4NzMsIm5iZiI6MTY4OTA0MDg3MywiZXhwIjoxNjkwMjUwNDczLCJpc3MiOiJTdXJyZWFsREIiLCJOUyI6InRlc3QiLCJEQiI6InRlc3QiLCJTQyI6ImFsbHVzZXJzIiwiSUQiOiJ1c2VyOmxsZWozdDhpOXAycGIxazdubWNuIn0.vL_bl90Np5CDGvcTVVpH54rCsmyCIWOBWm-coIS7aT16_tuKV0xXyTBbPOzE3G9k5skikWRvteO_6z04sjhiCA",
"message": "Registered"
}
To login send a POST request to /login
. The body of the request should be a JSON object with a username
and password
property. The /login
endpoint will return a JWT token that can be used to authenticate the user.
// JavaScript usage
const body = {
username: "notSpiderman",
password: "muhGuire",
};
const url = "http://localhost:4000/api/v1/login";
let response = await fetch(url, {
method: "POST",
body: JSON.stringify(body),
});
let data = await response.json();
console.log(data);
Response:
{
"status": "OK",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE2ODkwNDA5NzQsIm5iZiI6MTY4OTA0MDk3NCwiZXhwIjoxNjkwMjUwNTc0LCJpc3MiOiJTdXJyZWFsREIiLCJOUyI6InRlc3QiLCJEQiI6InRlc3QiLCJTQyI6ImFsbHVzZXJzIiwiSUQiOiJ1c2VyOnVwdWoweWlmeXoxMWlyeHZrd3djIn0.hnqwkgqTJ5X16hc89JWYex0epEecsROtvGsjGmg5FCD6nocgJP6PXgRRokRDwdi5OVQxWiQfmHxCZxieI4P0Bw",
"message": "Signed in"
}
To get user details send a GET request to /me
. The request should include a Authentication
header with a JWT token. The /me
endpoint will return the user details.
// JavaScript usage
const headers = {
Authentication:
"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE2ODkwNDA5NzQsIm5iZiI6MTY4OTA0MDk3NCwiZXhwIjoxNjkwMjUwNTc0LCJpc3MiOiJTdXJyZWFsREIiLCJOUyI6InRlc3QiLCJEQiI6InRlc3QiLCJTQyI6ImFsbHVzZXJzIiwiSUQiOiJ1c2VyOnVwdWoweWlmeXoxMWlyeHZrd3djIn0.hnqwkgqTJ5X16hc89JWYex0epEecsROtvGsjGmg5FCD6nocgJP6PXgRRokRDwdi5OVQxWiQfmHxCZxieI4P0Bw",
};
const url = "http://localhost:4000/api/v1/me";
let response = await fetch(url, {
method: "GET",
headers: headers,
});
let data = await response.json();
console.log(data);
Response:
{
"status": "OK",
"details": {
"created": "2023-07-10T18:52:02.533Z",
"email": "[email protected]",
"id": "user:upuj0yifyz11irxvkwwc",
"settings": { "marketing": true },
"user": "notSpiderman"
},
"message": "Success"
}
To create a new record send a POST request to /post
. The Authentication
header should contain a valid JWT token. The body of the request should be a JSON object with a name
and type
property. The name
property is the name of the record and the type
property is the type of record. The type
property is used to determine which table the record is stored in. To use this record in the frontend use type:name
as the id
of the record.
// JavaScript usage
let token = local.storage.getItem("token"); //JWT token from login or signup
const headers = {
Authentication: "Bearer " + token,
};
const body = {
name: "Black Eyed Peas", //required
type: "recipe", //required
description: "A delicious recipe for black eyed peas",
};
const url = "http://localhost:4000/api/v1/post";
let response = await fetch(url, {
method: "POST",
body: JSON.stringify(body),
});
let data = await response.json();
console.log(data);
Response:
{
"message": "Success",
"id": "recipe:Black_Eyed_Peas"
}
If a record already exists with the same name and type a new record will be created with a date appended to the end of the id.
{
"message": "Success",
"id": "recipe:Black_Eyed_Peas_1689039003963"
}
To get a record send a GET request to /get
. The head of the request should have a X-RCIAD-Requested-ID
header with optional x-rciad-limit
, x-rciad-page
, and x-rciad-subscribed
headers. The X-RCIAD-Requested-ID
header is the id of the record. To use the data contianed in the record use data.page[0]
. For the total number of records in the request use data.count
.
// header syntax
// X-RCIAD-Requested-ID: string
// x-rciad-limit: number defaults to 10
// x-rciad-page: number defaults to 1
// x-rciad-subscribed: comma seperated list of ids
// JavaScript usage
const headers = {
X-RCIAD-Requested-ID: "recipe:Black_Eyed_Peas",
x-rciad-limit: 10,
x-rciad-page: 1,
x-rciad-subscribed: "user:upuj0yifyz11irxvkwwc,user:8ka0jyifyz11irxvkwwe",
};
const url = "http://localhost:4000/api/v1/get";
let response = await fetch(url, {
method: "GET",
headers: headers,
});
let data = await response.json();
console.log(data);
Response:
{
"message": "Success",
"page": [
{
"id": "recipe:Black_Eyed_Peas",
"name": "Black Eyed Peas",
"type": "recipe",
"description": "A delicious recipe for black eyed peas",
"created_at": "2023-07-10T18:52:02.533Z",
"created_by": "user:upuj0yifyz11irxvkwwc",
"author": "notSpiderman"
}
],
"count": 1
}
To get multiple records of the same type send a GET request to /get
. The head of the request should have a X-RCIAD-Requested-ID
header with optional x-rciad-limit
, x-rciad-page
, and x-rciad-subscribed
headers. The X-RCIAD-Requested-ID
header is the id of the record. To use these records in the frontend use data.page
.
// JavaScript usage
const headers = {
X-RCIAD-Requested-ID: "recipe",
x-rciad-limit: 10, //defaults to 10 optional
x-rciad-page: 1, //defaults to 1 optional
x-rciad-subscribed: "user:upuj0yifyz11irxvkwwc,user:8ka0jyifyz11irxvkwwe", //filter by creator optional
};
const url = "http://localhost:4000/api/v1/get";
let response = await fetch(url, {
method: "POST",
headers: headers,
});
let data = await response.json();
console.log(data);
Response:
{
"message": "Success",
"page": [
{
"id": "recipe:Black_Eyed_Peas",
"name": "Black Eyed Peas",
"type": "recipe",
"description": "A delicious recipe for black eyed peas",
"created_at": "2023-07-10T18:52:02.533Z",
"created_by": "user:upuj0yifyz11irxvkwwc",
"author": "notSpiderman"
},
{
"id": "recipe:Black_Eyed_Peas_1689039003963",
"name": "Black Eyed Peas",
"type": "recipe",
"description": "A delicious recipe for black eyed peas",
"created_at": "2023-07-10T18:55:03.821Z",
"created_by": "user:upuj0yifyz11irxvkwwc",
"author": "notSpiderman"
}
],
"count": 2
}
To delete a record send a DELETE request to /delete
. The head should have an Authentication
header with a valid JWT token. The body of the request should have an id
property. The id
property is the id of the record.
// JavaScript usage
const token = localStorage.getItem("token"); // get token from local storage
const headers = {
Authentication: "Bearer " + token,
};
const body = {
id: "recipe:Black_Eyed_Peas",
};
const url = "http://localhost:4000/api/v1/delete";
let response = await fetch(url, {
method: "DELETE",
headers: headers,
body: JSON.stringify(body),
});
Response:
{
"status": "OK",
"message": "recipe:Black_Eyed_Peas was deleted"
}
To upload a file send a POST request to /upload
. The body of the request should be a form data object. To use this file in the frontend use the response as a URL.
NOTE: This is not best practice and should only be used for development purposes. In production you should use a CDN.
// JavaScript usage
async function uploadFiles(form) {
let attachment = {};
let formData = new FormData();
formData.append("attachment", form);
await fetch(`http://localhost:4000/api/v1/upload`, {
cache: "no-store",
method: "POST",
body: formData,
})
.then((res) => res.text())
.then((data) => (attachment = { name: form.name, url: data }));
return attachment;
}
console.log(await uploadFiles(file));
Response:
{
"name": "Black Eyed Peas Image",
"url": "http://localhost:4000/api/v1/files/Black_Eyed_Peas.jpg"
}
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
(in progress)
- Basic CRUD operations
- Logout features
- Update user details
- Bolster security
- Add more authentication methods
- Delete Records
- Move usage to a wiki or hosted documentation