Search code examples
javascriptapirestgraphqlstrapi

How to include a field in API without adding the key/val in database?


I am looking for a way the Strapi API returns a key/value pair which is calculate and added to the server response but not fetched from the Database:

I have done something like that using ApolloServer with Prisma. If you are wondering how I have done? then here is my setup:

Note: I am not looking for how slug feature in Strapi but in general I like to know how dynamic/calculate fields can be added to Strapi API Server response.

file: ./src/resolvers/Team.js

const Team = {
  slug(parent) {
    return parent.title.replace(/\s+/g, '-').toLowerCase();
  },
};

export { Team as default };

file: ./src/resolvers/index.js

import { extractFragmentReplacements } from 'prisma-binding';
import Query from './Query';
import Mutation from './Mutation';
import Team from './Team';

const resolvers = {
  Query,
  Mutation,

  // static
  Team,
};

const fragmentReplacements = extractFragmentReplacements(resolvers);
export { resolvers, fragmentReplacements };

file: ./src/prisma.js

import { Prisma } from 'prisma-binding';
import { fragmentReplacements } from './resolvers/index';

require('dotenv').config({ path: './.env' });

const prisma = new Prisma({
  typeDefs: 'src/generated/prisma.graphql',
  endpoint: process.env.API_URL,
  secret: process.env.PRISMA_MANAGEMENT_API_SECRET,
  fragmentReplacements,
});

export { prisma as default };

file: ./src/schema.graphql

type Team {
  id: ID!
  title: String!
  color: String!
  slug: String! // this is not in Db but taken care by `./src/resolvers/StaticTeam.js`
}

As you can see above that how I get the slug though that's not in database. I just like to have calculated key:val added to my API.


Solution

  • I like to know how dynamic/calculate fields can be added to Strapi API Server response

    Basically you are looking for a Custom data response, most likely combined with a custom endpoint(since you don't want to override an existing endpoint).

    You can do this by extending the existing API, lets break it down:

    1. Add a custom API endpoint
      a. Add routing
      b. Add handler
      c. Add permissions
    2. Run some logic
    3. Return a custom response

    (1) To add a custom API endpoint to a user defined content type, you need to (a) add routing in the following directory:

    ./api/{content-type}/config/routes.json
    

    Like so (in routes array):

    {
      "method": "GET",
      "path": "/teams/customData",
      "handler": "team.findCustomHandler",
      "config": {
        "policies": []
      }
    }
    

    (b)add a method in the following directory:

    ./api/{content-type}/controllers/{Content-Type}.js
    

    Like so:

    'use strict';
    module.exports = {
        async findCustomHandler(ctx) {
          //your logic here
        }
    };
    

    You can use the original find method to start off and add your values using your logic (this is a great example):

      async find(ctx) {
        let entities;
        if (ctx.query._q) {
          entities = await strapi.services.team.search(ctx.query);
        } else {
          entities = await strapi.services.team.find(ctx.query);
        }
        // TODO: add your extra calculated value
        return entities.map(entity => {
        // You can do this here
        sanitizeEntity(entity, { model: strapi.models.restaurant }));
        }
      }
    

    Docs you can check out:
    Extending a Model Controller

    Questions welcome