npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

remix-params-helper

v0.5.1

Published

This package makes it simple to use Zod with standard URLSearchParams and FormData which are typically used in Remix apps.

Downloads

18,606

Readme

Remix Params Helper

All Contributors

This package makes it simple to use Zod with standard URLSearchParams and FormData which are typically used in Remix apps.

🚨 Breaking Change v0.3.0

Helper no longer requires explicit types on helper. Thanks @zolrath. This will definitely cut down on the boilerplate.

🚧 Work in progress

This package is still work in progress. I'll be refining the API and fixing the TypeScript types.

🛠 Installation

npm install remix-params-helper zod

Zod is a peer dependency

📦 Zod

Zod is used to validate untyped data and either return a valid object or a list of errors encounted.

To use the helper, first define your Zod schema. It also supports nested objects and arrays.

const ParamsSchema = z.object({
  a: z.number(),
  b: z.string(),
  c: z.boolean(),
  d: z.string().optional(),
  e: z.array(z.number()),
})

📝 API Reference

getParams(params, schema)

This function is used to parse and validate data from URLSearchParams, FormData, or Remix params object.

It returns an object that has success property. If result.success is true then result.data will be a valid object of type T, inferred from your Zod schema.

Otherwise, result.errors will be an object with keys for each property that failed validation. The key value will be the validation error message.

NOTE: Error messages will now return the message from directly Zod. You can customize the error message in your Zod schema Zod Custom Error Messages

If the validation returns multiple errors for the same key, it will return an array, otherwise it will be a string.

errors[key] = 'message'
errors[key] = ['message 1', 'message 2']

Unlike Object.fromEntries(), this function also supports multi-value keys and will convert them to an array. So e=1&e=2&e=3 will convert it to e: [1,2,3]

const url = new URL(request.url)
const result = getParams(url.searchParams, ParamsSchema)
if (!result.success) {
  throw new Response(result.errors, { status: 400 })
}
// these variables will be typed and valid
const { a, b, c, d, e } = result.data

getSearchParams(request, schema)

This helper function is used to parse and validate URLSearchParams data from the Request found in the Remix action/loader, it returns the same result values as getParams.

const result = getSearchParams(request, ParamsSchema)
if (!result.success) {
  return json(result.errors, { status: 400 })
}
// these variable will be typed and valid
const { a, b, c, d, e } = result.data

getFormData(request, schema)

This helper function is used to parse and validate FormData data from the Request found in the Remix action/loader, it returns the same result values as getParams.

const result = await getFormData(request, ParamsSchema)
if (!result.success) {
  return json(result.errors, { status: 400 })
}
// these variables will be typed and valid
const { a, b, c, d, e } = result.data

✨ New in v0.4.2 Added *OrFail() versions of the helpers

The functions getParamsOrFail(), getFormDataOrFail(), getSearchParamsOrFail() will throw an Error when parsing fails. Since the helper can only return a valid result, the return value is always the data.

// returns valid data that can be destructured or Error is thrown
const { a, b, c, d, e } = await getFormDataOrFail(request, ParamsSchema)

NOTE: Although we provide these helpers, it is recommended that you return errors instead of throwing. Form validation is typically an expected error. Throwing Error should be reserved for unexpected errors.

✨ New in v0.4.0 Support for nested objects and arrays

Input names should be dot-separated (e.g, address.street). Array names can include the square brackets (e.g., favoriteFoods[]). These are optional. The helper will correctly determine if the value is an array.

describe('test nested objects and arrays', () => {
  it('should validate nested object', () => {
    const mySchema = z.object({
      name: z.string(),
      address: z.object({
        street: z.string(),
        city: z.string(),
        state: z.string(),
        zip: z.string(),
      }),
    })
    const formData = new FormData()
    formData.set('name', 'abcdef')
    formData.set('address.street', '123 Main St')
    formData.set('address.city', 'Anytown')
    formData.set('address.state', 'US')
    formData.set('address.zip', '12345')
    const result = getParams(formData, mySchema)
    expect(result.success).toBe(true)
    expect(result.data.address.street).toBe('123 Main St')
  })
  it('should validate arrays with [] syntax', () => {
    const mySchema = z.object({
      name: z.string(),
      favoriteFoods: z.array(z.string()),
    })
    const formData = new FormData()
    formData.set('name', 'abcdef')
    formData.append('favoriteFoods[]', 'Pizza')
    formData.append('favoriteFoods[]', 'Tacos')
    formData.append('favoriteFoods[]', 'Hamburgers')
    formData.append('favoriteFoods[]', 'Sushi')
    const result = getParams(formData, mySchema)
    expect(result.success).toBe(true)
    expect(result.data.favoriteFoods?.length).toBe(4)
  })
})

useFormInputProps(schema)

This helper allows you to set the props on your form <input/> based on your Zod schema.

The function returns another function that you use to spread the properties on your input. It currently sets the following props based on the key value you specify. If you need to override any of the props, just add it after you spread.

  • name
  • type: text, number, checkbox, date, email, url
  • required: not .optional()
  • min/max: number
  • minlength/maxlength: text
  • pattern: regex

If the key doesn't exist in the schema, it will throw an error. This way if you rename any properties, it will force you to use the correct key.

This currently uses the native browser validation like required. I plan on adding enhanced client-side validation that will utilize the same Zod schema.

function Component() {
  const inputProps = useFormInputProps(schema)

  return (
    <Form>
      <input ...{inputProps('a')} />
      <input ...{inputProps('b')} />
      <input ...{inputProps('c')} />
      {/* This will throw an error since 'x' is not in schema*/}
      <input ...{inputProps('x')} />
    </Form>
  )
}

🌎 Example App

There is an example app at https://remix-params-helper.herokuapp.com/

Click on the Actions demo and the URL Params demo to see the helper in action.

😍 Contributors

Thanks goes to these wonderful people (emoji key):

This project follows the all-contributors specification. Contributions of any kind welcome!