3. REST API endpoint

REST API endpoint

This file was copied from the packages/webapp/src/stories/docs folder. Its accuracy as of August 8, 2024 has not been verified

Many endpoints are not following LiteFarm API naming conventions. Please reach out to Kike if you have doubt.

| Resources | | --------- | | Express routing | | LiteFarm REST API styleguide |

1. Create new endpoints

Create endpoints to access/modify data in a new table

Go to directory /packages/api/routes and create a new file newTalbeNameRoute.js.

Copy the following template into the new file.

hasFarmAccess middleware and checkScope middleware are used for authorization, which will be covered by MiddleWare. Leave them as-is for now.

const express = require('express'); const router = express.Router(); const { getNewEntitiesByFarmId, getNewEntityByNewEntityId, deleteNewEntity, createNewEntity, updateNewEntity } = require('../controllers/newEntityController'); const checkScope = require('../middleware/acl/checkScope'); const hasFarmAccess = require('../middleware/acl/hasFarmAccess'); router.get('/farm/:farm_id', hasFarmAccess({ params: 'farm_id' }), checkScope(['get:new_entity']), getNewEntitiesByFarmId()); router.get('/:new_entity_id', hasFarmAccess({ params: 'new_entity_id' }), checkScope(['get:new_entity']), getNewEntityByNewEntityId()); router.delete('/:new_entity_id', hasFarmAccess({ params: 'new_entity_id' }), checkScope(['delete:new_entity']), deleteNewEntity()); router.post('/', hasFarmAccess({ body: 'farm_id' }), checkScope(['add:new_entity']), createNewEntity()); router.put('/:new_entity_id', hasFarmAccess({ params: 'new_entity_id' }), checkScope(['edit:new_entity']), updateNewEntity()); module.exports = router;

Copy

Open api/src/server.js, import created file newTalbeNameRoute.js, and attach new endpoints to express server.

const newEntityRoutes = require('./routes/newTalbeNameRoute'); const app = express(); app.use('/new_entity', newEntityRoutes);

Copy

Users will then be able to get new entities by accessing domain.org/new_entity/farm/farm_id;

Please refer to LiteFarm REST API styleguide for naming convention

In short, endpoints should follow /new_entity_table_name/new_entity_id for single pk and /composite_table_name/parent_table_name/parent_id/child_table_name/child_id for composite pk

A composite key example would be /user_farm/farm/farm_id/user/user_id where a farm has many owners/managers/extension officers/workers

2. Create empty controllers

Create a new file api/src/controller/newEntityController.js and copy the following template

const NewEntityModel = require('../models/newEntityModel'); const newEntityController = { getNewEntitiesByFarmId() { return async (req, res, next) => { const { farm_id } = req.params; try{ const result = await NewEntityModel.query().where(); return result?.length ? res.status(200).send(result): res.status(404).send('New entities not found'); }catch(error){ return res.status(400).json({ error }); } } }, getNewEntityByNewEntityId() { return async (req, res, next) => { const { new_entity_id } = req.params; try{ const result = await NewEntityModel.query().where(); return result ? res.status(200).send(result): res.status(404).send('New entity not found'); }catch(error){ return res.status(400).json({ error }); } } }, deleteNewEntity() { return async (req, res, next) => { const { new_entity_id } = req.params; try { const result = await NewEntityModel.query().where(); return result ? res.sendStatus(200): res.status(404).send('New entity not found'); } catch (error) { return res.status(400).json({ error }); } } }, createNewEntity(asset) { return async (req, res, next) => { try { const result = await NewEntityModel.query().where(); return res.status(201).send(result); } catch (error) { return res.status(400).json({ error }); } } }, updateNewEntity(asset) { return async (req, res, next) => { const { new_entity_id } = req.params; try { const result = await NewEntityModel.query().where(); return result ? res.status(200).send(result): res.status(404).send('New entity not found'); } catch (error) { return res.status(400).send({ error }); } } }, } module.exports = newEntityController;

Copy

Please refer to LiteFarm REST API styleguide for status status code convention

In short, post success should return status code 201.

get delete patch put success should return status code 200.

If no entity is found, return status code 404.

Validation errors should return status code 400.

Authorization (role permission) errors should return status code 403.

Authentication (invalid jwt) error should return error code 401

Steps for a backend - frontend story from a data flow perspective

  • Create knex migration to create/modify tables in database

  • Create objection models

  • Endpoints and empty controllers

  • Backend jest unit testing

  • Implement controllers

  • Create authorization/validation middlewares

  • Run tests and make sure everything passes

  • Create pure frontend components in storybook

  • res.data normalizer, redux slice, redux selectors to hold and access the data

  • Redux saga for get/post/put/patch request

  • Container component to connect saga actions/selector with pure components

  • Connect Route component with container components with react router

  • Manual testing