# Metadata

From https://trpc.io/docs/server/metadata

Procedure metadata allows you to add an optional procedure specific `meta` property which will be available in all [middleware](https://trpc.io/docs/server/middlewares) function parameters.

> [!TIP]
> Use metadata together with [`trpc-openapi`](https://github.com/jlalmes/trpc-openapi) if you want to expose REST-compatible endpoints for your application.


## Create router with typed metadata

```tsx no-check
import { initTRPC } from '@trpc/server';

type Context = { user: { name: string } | null };

interface Meta {
  authRequired: boolean;
}

export const t = initTRPC.context<Context>().meta<Meta>().create();

export const appRouter = t.router({
  // [...]
});
```

## Example with per route authentication settings

```tsx no-check title="server.ts"
import { initTRPC, TRPCError } from '@trpc/server';

type Context = { user: { name: string } | null };

interface Meta {
  authRequired: boolean;
}

export const t = initTRPC.context<Context>().meta<Meta>().create();

export const authedProcedure = t.procedure.use(async (opts) => {
  const { meta, next, ctx } = opts;
  // only check authorization if enabled
  if (meta?.authRequired && !ctx.user) {
    throw new TRPCError({ code: 'UNAUTHORIZED' });
  }
  return next();
});

export const appRouter = t.router({
  hello: authedProcedure.meta({ authRequired: false }).query(() => {
    return {
      greeting: 'hello world',
    };
  }),
  protectedHello: authedProcedure.meta({ authRequired: true }).query(() => {
    return {
      greeting: 'hello-world',
    };
  }),
});
```

## Default meta, chaining, and shallow merging

You can set default values for your meta type, and if you chain meta on top of a base procedure it will be shallow merged.

```tsx no-check
import { initTRPC } from '@trpc/server';

type Context = { user: { name: string } | null };

interface Meta {
  authRequired?: boolean;
  role?: 'user' | 'admin'
}

export const t = initTRPC
  .context<Context>()
  .meta<Meta>()
  .create({
    // Set a default value
    defaultMeta: { authRequired: false }
  });

const authMiddleware = t.middleware((opts) => opts.next());

const publicProcedure = t.procedure
// ^ Default Meta: { authRequired: false }

const authProcedure = publicProcedure
  .use(authMiddleware)
  .meta({
    authRequired: true,
    role: 'user'
  });
// ^ Meta: { authRequired: true, role: 'user' }

const adminProcedure = authProcedure
  .meta({
    role: 'admin'
  });
// ^ Meta: { authRequired: true, role: 'admin' }
```
