Skip to content

Releases: unnoq/orpc

v0.30.0

14 Jan 13:35
Compare
Choose a tag to compare

🌟 Improvement 🌟

  • Implement .errors in every builder, allowing you to define error handling at a global or granular level for enhanced reusability and flexibility.
  • Middleware use before .input/.output will be execute before/after validation step
const pub = os.errors({
  UNAUTHORIZED: {},
}).use(({ errors }) => {
  throw errors.UNAUTHORIZED()
})

const getting = pub
  .input(z.object({ id: z.string() }))
  .errors({
    NOT_FOUND: {
      message: 'User not found',
    },
  })
  .handler(({ errors }) => {
    throw errors.NOT_FOUND()
    throw errors.UNAUTHORIZED()
    // ...
  })

   πŸš¨ Breaking Changes

  • server: Middleware can run both before/after validation step  -  by @unnoq in #83 (cba52)

   πŸš€ Features

    View changes on GitHub

v0.29.0

11 Jan 13:14
Compare
Choose a tag to compare

🚨 Breaking Changes Alert 🚨

  • procedure is now completely non-callable by default (use the new .callable or .actionable methods for that).
  • createProcedureClient and createRouterClient now accept two arguments instead of one as before.
  • ORPCHandler and ORPCLink now become RPCHandler and RPCLink

πŸš€ New Features πŸš€

  • New .callable method to make a procedure callable like a regular function.
  • New .actionable method, similar to .callable(), but type-compatible with server actions (yes, server actions work again).
  • New call() helper function to directly call any procedure without creating a client.
'use server'

import { os } from '@orpc/server'
import { z } from 'zod'

export const getting = os
  .input(z.object({
    name: z.string(),
  }))
  .output(z.string())
  .handler(async ({ input }) => {
    return `Hello ${input.name}`
  })
  .actionable()

// from client just call it as a function
const onClick = async () => {
  const result = await getting({ name: 'Unnoq' })
  alert(result)
}

   πŸš¨ Breaking Changes

    View changes on GitHub

v0.28.0

10 Jan 13:01
Compare
Choose a tag to compare

🀯 End-to-End Typesafe Error Handling 🀯

const createPost = os
  .errors({
    CONFLICT: {
      data: z.object({
        why: z.string(),
      }),
    },
  })
  .handler(({ errors }) => {
    throw errors.CONFLICT({ data: { why: 'some reason' } })
  })

const [data, error] = await safe(createPost({ title: 'title' }))

if (error && isDefinedError(error)) {
  const data = error.data // { why: 'some reason' } full typed data
}

const mutation = useMutation(orpc.createPost.mutationOptions({
  onError(error) {
    if (isDefinedError(error)) {
      const data = error.data // { why: 'some reason' } full typed data
    }
  },
}))
  • Removed @orpc/react since it was too complicated to maintain, while @orpc/react-query is simpler and easier to maintain while achieve the same functionality.
  • Temporarily removed @orpc/next due to unexpected behavior from Next.js. You can follow progress here. I will bring this package back in a future oRPC version.

   πŸš¨ Breaking Changes

  • End-to-end typesafe error handling, remove @orpc/react and @orpc/next  -  by @unnoq in #77 (9b3a0)
    View changes on GitHub

v0.27.0

06 Jan 12:43
Compare
Choose a tag to compare

🚨 Breaking Changes Alert 🚨

  • The CompositeHandler has been removed. Instead, we recommend using separate base paths for each handler.
  • Handlers will no longer automatically handle cases where no procedure matches. You are now responsible for returning a 404response or falling back to other routes.
  • Official Adapters for Next.js and Hono.js
if (url.pathname.startsWith('/api')) {
   const { matched, response } = await openapiHandler.handle(request, {
     prefix: '/api'
   })
 
   if (matched) {
     return response
   }
}
 
if (url.pathname.startsWith('/rpc')) {
  const { matched, response } = await orpcHandler.handle(request, {
    prefix: '/rpc'
  })
 
  if (matched) {
    return response
  }
}

return new Response('Not Found', { status: 404 })

   πŸš¨ Breaking Changes

  • server, openapi: Rewrite ORPCHandler, OpenAPIHandler  -  by @unnoq in #74 (911bd)

   πŸš€ Features

    View changes on GitHub

v0.26.0

04 Jan 13:12
Compare
Choose a tag to compare

🚨 Breaking Changes

Middleware, and handler parameters has been redesigned.

os.handler((input, context, meta) => { }) // πŸ›‘ old
os.handler(({ context, input }) => { }) // βœ… new

os.middleware((input, context, meta) => meta.next({})) // πŸ›‘ old
os.middleware(({ context, next }, input) => next({})) // βœ… new

os.use((input, context, meta) => meta.next({})) // πŸ›‘ old
os.use(({ context, next }, input) => next({})) // βœ… new

   πŸš¨ Breaking Changes

  • server: Redesign middleware and handler parameters  -  by @unnoq in #72 (7e55b)
    View changes on GitHub

v0.25.0

03 Jan 13:06
Compare
Choose a tag to compare

πŸš€ New Features πŸš€

  1. configGlobal used to change some default oRPC behavior
import { configGlobal } from '@orpc/contract'; // or '@orpc/server'

configGlobal({
  defaultMethod: 'GET', // Default HTTP method for requests
  defaultSuccessStatus: 200, // Default HTTP status for successful responses
  defaultInputStructure: 'compact', // Input payload structure: 'compact' or 'expanded'
  defaultOutputStructure: 'compact', // Output payload structure: 'compact' or 'expanded'
});
  1. successDescripton option, allow use customize the success description when generate OpenAPI specs
const ping = oc.route({ successDescripton: 'Indicate the system is OK' })

   πŸš¨ Breaking Changes

  • contract: Spread merge route when redefine  -  by @unnoq (67ca7)

   πŸš€ Features

   πŸž Bug Fixes

    View changes on GitHub

v0.24.0

02 Jan 13:33
Compare
Choose a tag to compare

πŸš€Official Node.js Adapter πŸŽ‰

import { createServer } from 'node:http'
import { OpenAPIServerlessHandler } from '@orpc/openapi/node'
import { CompositeHandler, ORPCHandler } from '@orpc/server/node'

const openapiHandler = new OpenAPIServerlessHandler(router, {
  schemaCoercers: [
    new ZodCoercer(),
  ],
})

const server = createServer((req, res) => {
  if (req.url?.startsWith('/api')) {
    return openapiHandler.handle(req, res, {
      prefix: '/api',
      context: {},
    })
  }

  res.statusCode = 404
  res.end('Not found')
})

   πŸš€ Features

   πŸž Bug Fixes

  • openapi: Silence ignore dangerous error when generate openapi spec  -  by @unnoq (73eb9)
    View changes on GitHub

v0.23.0

01 Jan 14:41
Compare
Choose a tag to compare

πŸš€ New Feature: Input/Output Structure πŸŽ‰

Simplify or customize how your API handles data with Input/Output Structure!

🌟 Highlights

  • inputStructure:

    • compact: Merges params + query/body.
    • detailed: Keeps params, query, headers, and body separate.
  • outputStructure:

    • compact: Returns only the body.
    • detailed: Separates headers and body.

Example

os.route({
  inputStructure: 'detailed',
  outputStructure: 'detailed',
})
  .input(z.object({
    params: z.object({ id: z.string() }),
    query: z.object({ search: z.string() }),
    body: z.object({ name: z.string() }).optional(),
  }))
  .handler((input) => ({
    body: { message: 'Hello' },
    headers: { 'x-header': 'value' },
  }));

   πŸš€ Features

   πŸž Bug Fixes

  • zod: Zod to json schema not covert zod description  -  by @unnoq (1cee9)
    View changes on GitHub

v0.22.0

31 Dec 10:56
Compare
Choose a tag to compare

πŸš€ Official Vue Pinia Colada integration now available πŸš€

import { useMutation, useQuery, useQueryCache } from '@pinia/colada'
import { orpc } from 'examples/vue-colada'
 
// Fetch data
const { data: gettingData } = useQuery(orpc.getting.queryOptions({ input: { name: 'unnoq' } }))
 
// Perform mutations
const { mutate: postMutate } = useMutation(orpc.post.create.mutationOptions())
 
// Invalidate queries
const queryCache = useQueryCache()
queryCache.invalidateQueries({ key: orpc.key() }) // Invalidate all queries
queryCache.invalidateQueries({ key: orpc.post.find.key({ input: { id: 'example' } }) }) // Specific queries

   πŸš€ Features

   πŸž Bug Fixes

  • react-query, vue-query: Incorrect type on queryFn  -  by @unnoq (011bc)
    View changes on GitHub

v0.21.0

30 Dec 09:52
Compare
Choose a tag to compare

🚨 Breaking Changes 🚨

  • .func is now .handler to better convey its purpose and functionality.

πŸš€ New Features πŸš€

1. successStatus for Routes

You can now use the successStatus option in .route to override the default success status in OpenAPI specifications.

2. Enhanced ORPCLink with Custom Methods

ORPCLink now supports defining custom methods, making the Client context more powerful and flexible.

type ClientContext = { cache?: RequestCache } | undefined;

// If `ClientContext` is not `undefinable`, it will enforce providing `context` in every call.

const orpcLink = new ORPCLink<ClientContext>({
  url: 'http://localhost:3000/api',

  // Optional headers for additional configurations
  headers: () => ({
    Authorization: 'Bearer token',
  }),

  fetch: (input, init, context) =>
    globalThis.fetch(input, {
      ...init,
      cache: context?.cache,
    }),

  method: (path, input, context) => {
    // Automatically switch between GET and POST based on input and context

    if (context?.cache) {
      return 'GET';
    }

    // Example: Use GET for paths ending with 'get', 'find', 'list', or 'search'
    if (['get', 'find', 'list', 'search'].includes(path.at(-1)!)) {
      return 'GET';
    }

    return 'POST';
  },
});

const client = createORPCClient<typeof router, ClientContext>(orpcLink);

// Example call with `context` specifying cache
client.getting({ name: 'unnoq' }, { context: { cache: 'force-cache' } });

These updates make it easier to build flexible and type-safe clients with more control over request handling.

   πŸš¨ Breaking Changes

  • client: DynamicLink pass only context instead of full option  -  by @unnoq (2ba06)
  • server: Rename .func() to .handler()  -  by @unteifu in #60 (5d3da)

   πŸš€ Features

  • client:
    • Support custom method for ORPCLink  -  by @unnoq (9ada8)
    • Add fallbackMethod & maxURLLength to ORPCLink  -  by @unnoq (b6be6)
  • openapi:
    View changes on GitHub