Future Eats Service is a backend service responsible for managing the endpoints consumed by Future Eats UI (https://github.com/pedro-severo/future-eats). It is a Node project built in typescript, exposed by a GraphQL interface and infraestructured with Firebase Services - including a non-SQL database (Firestore). The project is fully tested with jest (integration and unit tests), also with a coverage checking (100%) to push new changes
Follow these steps to set up and run Future Eats API on your local machine.
Before you begin, make sure you have the following installed:
- Git
- Node.js and npm - v.20
- Firebase SDK
-
(optional) Install yarn:
consider to install yarn command to use it in place of npm command:
npm install -g yarn
more information here: https://www.npmjs.com/package/yarn
-
Clone the repository:
git clone https://github.com/pedro-severo/future-eats-service.git
-
Navigate to the project directory:
cd future-eats-service
-
Install dependencies:
npm install
or
yarn install
-
Firebase setup
On
context.ts
file of database folder, you need change the path of this following line to point to the json file who gives you access on database:const serviceAccount = require('../../../../future-eats-service-5f069c811a09.json');
To generate this json file with the key to access the databe, take a look here: https://firebase.google.com/docs/admin/setup
-
Start the application:
npm run start
or
yarn start
-
Open graphql api
Paste http://localhost:3003/ on browser to open graphql api and to consume queries and mutations of application
The endpoint are stored inside modules folders. Now, there are these modules on the project:
- User
Folder structure of the project:
├── src/
│ ├── modules/
| | ├── {module_name}/
│ │ ├── controllers/ // gateway of endpoints: input checking and sanitization, response formating to be exposed on API
│ │ ├── entities/ // entities related to the module. Usualy, there is one main entity, like User entity on the user module, and some sub-entites
│ │ ├── graphql/ // all graphQL schema and types related the module
│ │ ├── repository/ // folder which stores the class who connect useCases with generic database service
│ │ ├── useCases/ => // self explanatory folder.
│ │ └── resolvers.ts => // file to register connect on GraphQL schema defined on graphql folder
│ ├── shared/
│ │ ├── database/
│ │ └── dependencies/ // dependency injection config
│ │ └── server/
│ │ └── services/
│ │ ├── authentication/ // authorization token management (token generating, checking, etc)
│ │ └── hash/ // password encripty service
│ │ └── uuid/ // id generation service
yarn (or npm) start
Starts GraphQL server at http://localhost:3003/
Person who makes orders on app:
export type UserType = {
id: string;
name: string;
email: string;
password: string;
hasAddress: boolean;
cpf: string;
mainAddressId?: string;
};
- UserAddress
export type UserAddressType = { id: string, city: string, complement: string, state: string, streetNumber: string, zone: string, streetName: string }
Signup
**Register a new user on database and return a token to be used on protected endpoints**
Response model:
{
"status": 201,
"data": {
"token": "Bearer token",
"user": {
"id": "string",
"name": "Severo Snape",
"email": "severo.snape@gmail.com",
"cpf": "000000000",
"hasAddress": false,
"password": "encriptedPassword",
}
}
}
Here there are the graphQL related types:
type Mutation {
signup(input: SignupInput): UserApiResponse!
}
input SignupInput {
name: String!
email: String!
cpf: String!
password: String!
}
type UserApiResponse {
status: Int!
data: UserResponse!
}
type UserResponse {
user: User!
token: String!
}
type User {
id: String!
name: String!
email: String!
cpf: String!
hasAddress: Boolean!
password: String!
}
Mutation example:
mutation signup(
$email: String!
$password: String!
$cpf: String!
$name: String!
) {
signup(
input: {
email: $email
password: $password
cpf: $cpf
name: $name
}
) {
status
data {
token
user {
password
name
id
hasAddress
email
cpf
}
}
}
}
Login
**Login flow. It returns a token to be used on protected endpoints.**
Response model:
{
"status": 200,
"data": {
"token": "Bearer token",
"user": {
"id": "string",
"name": "Severo Snape",
"email": "severo.snape@gmail.com",
"cpf": "000000000",
"hasAddress": false,
"password": "encriptedPassword",
}
}
}
Here there are the graphQL related types:
type Mutation {
login(input: LoginInput): UserApiResponse!
}
input LoginInput {
email: String!
password: String!
}
type UserApiResponse {
status: Int!
data: UserResponse!
}
type UserResponse {
user: User!
token: String!
}
type User {
id: String!
name: String!
email: String!
cpf: String!
hasAddress: Boolean!
password: String!
}
Mutation example:
mutation login(
$email: String!
$password: String!
) {
login(
input: {
email: $email
password: $password
}
) {
status
data {
token
user {
password
name
id
hasAddress
email
cpf
}
}
}
}
Register Address
Create a UserAddress linked with the User who make the request (1:n).
Response model:
{
"status": 201,
"data": {
"city": "Lisbon",
"complement": "1D",
"state": "Metropolitan Zone of Lisbon",
"streetNumber": "34",
"zone": "Campo de Ourique",
"streetName": "4 de Infantaria",
"id": "123"
}
}
Here there are the graphQL related types:
type Mutation {
registerAddress(input: RegisterAddressInput): RegisterAddressApiResponse!
}
input RegisterAddressInput {
userId: String!
city: String!
complement: String!
state: String!
streetName: String!
streetNumber: String!
zone: String!
}
type RegisterAddressApiResponse {
status: Int!
data: RegisterAddressResponse!
}
type RegisterAddressResponse {
city: String!
complement: String!
state: String!
streetNumber: String!
zone: String!
streetName: String!
id: String!
}
Mutation example:
mutation registerAddress(
$userId: String!
$city: String!
$complement: String
$state: String!
$streetName: String!
$streetNumber: String!
$zone: String!
) {
registerAddress(
input: {
userId: $userId
city: $city
complement: $complement
state: $state
streetName: $streetName
streetNumber: $streetNumber
zone: $zone
}
) {
status
data {
city
complement
state
streetNumber
zone
streetName
id
}
}
}
Authorization:
{
"Authorization": "Bearer token"
}
Get Profile
Give the common user (person who makes orders on app) details according this model:
{
"status": 202,
"data": {
"name": "Severo Snape",
"email": "severo.snape@email.com",
"cpf": "000000000",
"hasAddress": true,
"address": "Spinner's End, s/n, complement, Cokeworth, England"
}
}
The option to use "Profile" instead "User" to name the endpoint (getProfile) is because the system doesn't returns a User if we see to entity. Instead, the system formats a specific response according the two entities related to a common user (User and UserAddress entities), merging specific keys of this two entities to generate the response above.
Here there are the graphQL related types:
type Query {
getProfile(input: GetProfileInput): GetProfileApiResponse!
}
input GetProfileInput {
userId: String!
}
type GetProfileApiResponse {
status: Int!
data: GetProfileResponse!
}
type GetProfileResponse {
id: String!
name: String!
email: String!
cpf: String!
hasAddress: Boolean!
address: String
}
Query example:
getProfile(input: "") {
status
data {
id
name
email
cpf
hasAddress
address
}
}
Authorization:
{
"Authorization": "Bearer token"
}