---
title: "Integrate Within a Framework"
description: "Integrating FGA within a framework, such as Fastify or Fiber"
canonical: "https://openfga.dev/docs/getting-started/framework"
content_type: "documentation"
last_updated: "2026-08-24T10:26:12.000Z"
---

# Integrate Within a Framework

This section will illustrate how to integrate OpenFGA within a framework, such as [Fastify](https://www.fastify.io/) or [Fiber](https://docs.gofiber.io/).

## Before you start

- Node.js
- Go

1. Deploy an instance of the OpenFGA server, and have ready the values for your setup: FGA\_STORE\_ID, FGA\_API\_URL and, if needed, FGA\_API\_TOKEN.
2. You have [installed the OpenFGA SDK](https://openfga.dev/docs/getting-started/install-sdk.md).
3. You have [configured the _authorization model_](https://openfga.dev/docs/getting-started/configure-model.md) and [updated the _relationship tuples_](https://openfga.dev/docs/getting-started/update-tuples.md).
4. You know how to [perform a Check](https://openfga.dev/docs/getting-started/perform-check.md).
5. You have loaded `FGA_API_URL` and `FGA_STORE_ID` as environment variables.

1) Deploy an instance of the OpenFGA server, and have ready the values for your setup: FGA\_STORE\_ID, FGA\_API\_URL and, if needed, FGA\_API\_TOKEN.
2) You have [installed the OpenFGA SDK](https://openfga.dev/docs/getting-started/install-sdk.md).
3) You have [configured the _authorization model_](https://openfga.dev/docs/getting-started/configure-model.md) and [updated the _relationship tuples_](https://openfga.dev/docs/getting-started/update-tuples.md).
4) You know how to [perform a Check](https://openfga.dev/docs/getting-started/perform-check.md).
5) You have loaded `FGA_API_URL` and `FGA_STORE_ID` as environment variables.

## Step by step

Assume that you want to have a web service for `document`s using one of the frameworks mentioned above. The service will authenticate users via [JWT tokens](https://auth0.com/docs/secure/tokens/json-web-tokens), which contain the user ID.

Note

The reader should set up their own `login` method based on their OpenID connect provider's documentation.

Assume that you want to provide a route `GET /read/{document}` to return documents depending on whether the authenticated user has access to it.

### 01. Install and setup framework

The first step is to install the framework.

- Node.js
- Go

For the context of this example, we will use the [Fastify framework](https://www.fastify.io/). For that we need to install the following packages:

- the [`fastify`](https://github.com/fastify/fastify) package that provides the framework itself
- the [`fastify-plugin`](https://github.com/fastify/fastify-plugin) package that allows integrating plugins with Fastify
- the [`fastify-jwt`](https://github.com/fastify/fastify-jwt) package for processing JWT tokens

Using [npm](https://www.npmjs.com):

```
npm install fastify fastify-plugin fastify-jwt
```

Using [yarn](https://yarnpkg.com):

```
yarn add fastify fastify-plugin fastify-jwt
```

Next, we setup the web service with the `GET /read/{document}` route in file `app.js`.

```
// Require the framework and instantiate it

const fastify = require('fastify')({ logger: true });



// Declare the route

fastify.get('/read/:document', async (request, reply) => {

  return { read: request.params.document };

});



// Run the server

const start = async () => {

  try {

    await fastify.listen(3000);

  } catch (err) {

    fastify.log.error(err);

    process.exit(1);

  }

};

start();
```

For the context of this example, we will use the [Fiber framework](https://docs.gofiber.io/). For that we need to install the following Go packages:

- the [`gofiber/fiber`](https://docs.gofiber.io/) package that provides the Fiber framework itself
- the [`gofiber/jwt`](https://github.com/gofiber/jwt) middleware authentication layer for JWT
- the [`golang-jwt`](https://github.com/golang-jwt/jwt) package that provides Go support for JWT

```
go get -u github.com/gofiber/fiber/v2 github.com/gofiber/jwt/v3 github.com/golang-jwt/jwt/v4
```

Next, we setup the web service with the `GET /read/{document}` route.

```
package main



import "github.com/gofiber/fiber/v2"



func main() {

  app := fiber.New()



  app.Get("/read/:document", read)



  app.Listen(":3000")

}



func read(c *fiber.Ctx) error {

  return c.SendString(c.Params("document"))

}
```

### 02. Authenticate and get user ID

Before we can call OpenFGA to protect the `/read/{document}` route, we need to validate the user's JWT.

- Node.js
- Go

The `fastify-jwt` package allows validation of JWT tokens, as well as providing access to the user's identity.

In `jwt-authenticate.js`:

```
const fp = require('fastify-plugin');



module.exports = fp(async function (fastify, opts) {

  fastify.register(require('fastify-jwt'), {

    secret: {

      private: readFileSync(`${path.join(__dirname, 'certs')}/private.key`, 'utf8'),

      public: readFileSync(`${path.join(__dirname, 'certs')}/public.key`, 'utf8'),

    },

    sign: { algorithm: 'RS256' },

  });



  fastify.decorate('authenticate', async function (request, reply) {

    try {

      await request.jwtVerify();

    } catch (err) {

      reply.send(err);

    }

  });

});
```

Then, use the `preValidation` hook of a route to protect it and access the user information inside the JWT:

In `route-read.js`:

```
module.exports = async function (fastify, opts) {

  fastify.get(

    '/read/:document',

    {

      preValidation: [fastify.authenticate],

    },

    async function (request, reply) {

      // the user's id is in request.user

      return { read: request.params.document };

    },

  );

};
```

Finally, update `app.js` to register the newly added hooks.

```
const fastify = require('fastify')({ logger: true });

const jwtAuthenticate = require('./jwt-authenticate');

const routeread = require('./route-read');



fastify.register(jwtAuthenticate);

fastify.register(routeread);



// Run the server!

const start = async () => {

  try {

    await fastify.listen(3000);

  } catch (err) {

    fastify.log.error(err);

    process.exit(1);

  }

}

start();
```

We will now setup middleware to authenticate the incoming JWTs.

```
package main



import (

  "crypto/rand"

  "crypto/rsa"

  "log"



  "github.com/gofiber/fiber/v2"



  jwtware "github.com/gofiber/jwt/v3"

  "github.com/golang-jwt/jwt/v4"

)



var (

  // Do not do this in production.

  // In production, you would have the private key and public key pair generated

  // in advance. NEVER add a private key to any GitHub repo.

  privateKey *rsa.PrivateKey

)



func main() {

  app := fiber.New()



  // Just as a demo, generate a new private/public key pair on each run.

  rng := rand.Reader

  var err error

  privateKey, err = rsa.GenerateKey(rng, 2048)

  if err != nil {

    log.Fatalf("rsa.GenerateKey: %v", err)

  }



  // JWT Middleware

  app.Use(jwtware.New(jwtware.Config{

    SigningMethod: "RS256",

    SigningKey:    privateKey.Public(),

  }))



  app.Get("/read/:document", read)



  app.Listen(":3000")

}



func read(c *fiber.Ctx) error {

  user := c.Locals("user").(*jwt.Token)

  claims := user.Claims.(jwt.MapClaims)

  name := claims["name"].(string)

  return c.SendString(name + " read " + c.Params("document"))

}
```

### 03. Integrate the OpenFGA check API into the service

- Node.js
- Go

First, we will create a decorator `preauthorize` to parse the incoming HTTP method as well as name of the document, and set the appropriate `relation` and `object` that we will call Check on.

In `preauthorize.js`:

```
const fp = require('fastify-plugin');



module.exports = fp(async function (fastify, opts) {

  fastify.decorate('preauthorize', async function (request, reply) {

    try {

      switch (request.method) {

        case 'GET':

          request.relation = 'reader';

          break;

        case 'POST':

          request.relation = 'writer';

          break;

        case 'DELETE':

        default:

          request.relation = 'owner';

          break;

      }

      request.object = `document:${request.params.document}`;

    } catch (err) {

      reply.send(err);

    }

  });

});
```

Next, we will create a decorator called `authorize`. This decorator will invoke the [Check API](https://openfga.dev/docs/getting-started/perform-check.md) to see if the user has a relationship with the specified document.

In `authorize.js`:

```
const fp = require('fastify-plugin');

const { OpenFgaClient } = require('@openfga/sdk'); // OR import { OpenFgaClient } from '@openfga/sdk';



module.exports = fp(async function (fastify, opts) {

  fastify.decorate('authorize', async function (request, reply) {

    try {

      // configure the openfga api client

      const fgaClient = new OpenFgaClient({

        apiUrl: process.env.FGA_API_URL, // required, e.g. https://api.fga.example

        storeId: process.env.FGA_STORE_ID,

      });

      const { allowed } = await fgaClient.check({

        user: request.user,

        relation: request.relation,

        object: request.object,

      });

      if (!allowed) {

        reply.code(403).send(`forbidden`);

      }

    } catch (err) {

      reply.send(err);

    }

  });

});
```

We can now update the `GET /read/{document}` route to check for user permissions.

In `route-read.js`:

```
module.exports = async function (fastify, opts) {

  fastify.get(

    '/read/:document',

    {

      preValidation: [fastify.authenticate, fastify.preauthorize, fastify.authorize],

    },

    async function (request, reply) {

      // the user's id is in request.user

      return { read: request.params.document };

    },

  );

};
```

Finally, we will register the new hooks in `app.js`:

```
const fastify = require('fastify')({ logger: true });

const jwtAuthenticate = require('./jwt-authenticate');

const preauthorize = require('./preauthorize');

const authorize = require('./authorize');

const routeread = require('./route-read');



fastify.register(jwtAuthenticate);

fastify.register(preauthorize);

fastify.register(authorize);

fastify.register(routeread);



const start = async () => {

  try {

    await fastify.listen(3000);

  } catch (err) {

    fastify.log.error(err);

    process.exit(1);

  }

}

start();
```

We will create two middlewares:

- `preauthorize` will parse the user's JWT and prepare variables needed to call Check API.
- `checkAuthorization` will call the [`Check API`](https://openfga.dev/docs/getting-started/perform-check.md) to see if the user has a relationship with the specified document.

```
package main



import (

  "context"

  "crypto/rand"

  "crypto/rsa"

  "log"

  "os"



  "github.com/gofiber/fiber/v2"



  jwtware "github.com/gofiber/jwt/v3"

  "github.com/golang-jwt/jwt/v4"

  . "github.com/openfga/go-sdk/client"

)



var (

  // Do not do this in production.

  // In production, you would have the private key and public key pair generated

  // in advance. NEVER add a private key to any GitHub repo.

  privateKey *rsa.PrivateKey

)



func main() {

  app := fiber.New()



  // Just as a demo, generate a new private/public key pair on each run.

  rng := rand.Reader

  var err error

  privateKey, err = rsa.GenerateKey(rng, 2048)

  if err != nil {

    log.Fatalf("rsa.GenerateKey: %v", err)

  }



  // JWT Middleware

  app.Use(jwtware.New(jwtware.Config{

    SigningMethod: "RS256",

    SigningKey:    privateKey.Public(),

  }))



  app.Use("/read/:document", preauthorize)



  app.Use(checkAuthorization)



  app.Get("/read/:document", read)



  app.Listen(":3000")

}



func read(c *fiber.Ctx) error {

  user := c.Locals("user").(*jwt.Token)

  claims := user.Claims.(jwt.MapClaims)

  name := claims["name"].(string)

  return c.SendString(name + " read " + c.Params("document"))

}



func preauthorize(c *fiber.Ctx) error {

  // get the user name from JWT

  user := c.Locals("user").(*jwt.Token)

  claims := user.Claims.(jwt.MapClaims)

  name := claims["name"].(string)

  c.Locals("username", name)



  // parse the HTTP method

  switch (c.Method()) {

    case "GET":

      c.Locals("relation", "reader")

    case "POST":

      c.Locals("relation", "writer")

    case "DELETE":

      c.Locals("relation", "owner")

    default:

      c.Locals("relation", "owner")

  }



  // get the object name and prepend with type name "document:"

  c.Locals("object", "document:" + c.Params("document"))

  return c.Next()

}



// Middleware to check whether user is authorized to access document

func checkAuthorization(c *fiber.Ctx) error {

  fgaClient, err := NewSdkClient(&ClientConfiguration{

    ApiUrl:               os.Getenv("FGA_API_URL"), // required, e.g. https://api.fga.example

    StoreId:        os.Getenv("FGA_STORE_ID"), // optional, not needed for \`CreateStore\` and \`ListStores\`, required before calling for all other methods

    AuthorizationModelId: os.Getenv("FGA_MODEL_ID"),  // optional, can be overridden per request

  })



  if err != nil {

    return fiber.NewError(fiber.StatusServiceUnavailable, "Unable to build OpenFGA client")

  }



  body := ClientCheckRequest{

    User: c.Locals("username").(string),

    Relation: c.Locals("relation").(string),

    Object: c.Locals("object").(string),

  }

  data, err := fgaClient.Check(context.Background()).Body(body).Execute()



  if err != nil {

    return fiber.NewError(fiber.StatusServiceUnavailable, "Unable to check for authorization")

  }



  if !(*data.Allowed) {

    return fiber.NewError(fiber.StatusForbidden, "Forbidden to access document")

  }



  // Go to the next middleware

  return c.Next()

}
```

## Related Sections

Take a look at the following sections for examples that you can try when integrating with SDK.

**Entitlements**

Modeling Entitlements for a System in OpenFGA.

- [More](https://openfga.dev/docs/modeling/advanced/entitlements.md)

**IoT**

Modeling Fine-Grained Authorization for an IoT Security Camera System with OpenFGA.

- [More](https://openfga.dev/docs/modeling/advanced/iot.md)

**Slack**

Modeling Authorization for Slack with OpenFGA.

- [More](https://openfga.dev/docs/modeling/advanced/slack.md)
