Skip to content

pedro-severo/future-eats-service

Repository files navigation

Future Eats Service

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

Summary

Follow these steps to set up and run Future Eats API on your local machine.

Prerequisites

Before you begin, make sure you have the following installed:

Setup

  1. (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

  2. Clone the repository:

    git clone https://github.com/pedro-severo/future-eats-service.git
  3. Navigate to the project directory:

    cd future-eats-service
  4. Install dependencies:

    npm install

    or

    yarn install     
    
  5. 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

  6. Start the application:

    npm run start

    or

    yarn start
  7. Open graphql api

    Paste http://localhost:3003/ on browser to open graphql api and to consume queries and mutations of application

Main technologies used

A description of the architecture

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

Running

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;
   };

Sub-entities:

  • 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"
}

About

A POC built with Node.js and TypeScript

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages