Unlock seamless workflows and faster delivery with our latest releases - Join the deep dive

Issue with uploading images to Sanity in a Next.js app and seeking help from the community.

43 replies
Last updated: Sep 27, 2022
Hey all, Ive set up an editor token for my Nextjs app but I'm getting
Upload failed: the mutation(s) failed: Insufficient permissions; permission "create" required
when trying to upload a file to
client.assets
Sep 26, 2022, 7:39 PM
It sounds like your app may not be picking up on your token. How have you set it up?
Sep 26, 2022, 7:42 PM
all im trying to do is this exact functionality of this todo app but with images as well
Sep 26, 2022, 7:43 PM
id literally pay some1 help me add this functionality lmao but im a dev so I wanna figure it myself... im close I think ? but its not super simple to add images to Sanity yet, harder than I thought.
Sep 26, 2022, 7:44 PM
Sep 26, 2022, 7:45 PM
basically just want to plug in a little file field and grab the image and add it to sanity along with the title, date, etc. in this todo app that's already made (essentially). Thanks for ANY advice you can offer!
Sep 26, 2022, 7:45 PM
I forked this repo to start, so I have this same repo, btw
Sep 26, 2022, 7:46 PM
i've set up the token and connect to the api and sending strings just fine this image is causing issues tho
Sep 26, 2022, 8:17 PM
sorry - to clarify if I try to upload the image from the page under
pages/todo.js
like using the front-end, then I get the permissions error, and if I try to send it to the
api/todo.js
api call then the
File
object turns into an empty object
{}
sorry to bother w such a trivial thing but im stuck šŸ˜•
Sep 26, 2022, 8:54 PM
I got ALL strings to publish to the API just fine, its just files that throw me this permissions error
Sep 26, 2022, 8:54 PM
user M
I am so sorry to bother you even a little -- really hope you could maybe help me identify whats up with the permissions because I might be able to take it from there myself heh
Sep 26, 2022, 9:01 PM
I said alot above but, simply put, I do have my token set up and I am able to publish strings but
client.assets
seems to have permissions issues, is there more I need to do for CORS or Token settings?
Sep 26, 2022, 9:01 PM
Can you share your specific code? It's difficult to know what's wrong without it.
Sep 26, 2022, 9:10 PM
its this repo exactly with my sanity credentials and my sanity token: https://github.com/bathrobe/next-magic-sanity-todo
Sep 26, 2022, 9:10 PM
I don't see where that's attempting to upload assets.
Sep 26, 2022, 9:15 PM
okay ill need to submit my code up to github 1 moment
Sep 26, 2022, 9:16 PM
sorry wish this was a simpler thing 1 moment plz
Sep 26, 2022, 9:16 PM
user M
here it is thank u, link below to where the file input field is
Sep 26, 2022, 9:20 PM
I know this is alot sorry šŸ˜• - so basically where do I need to be uploading the image? The
page
itself or the
/api/todo
call?
Sep 26, 2022, 9:24 PM
Because here im calling it on the page: https://github.com/outer-code/example-4-sanity/blob/5714fdc4f797df0c52d4815d390e0d969244a210/src/pages/todos.js#L74 -- maybe thats the permissions issue. but if I try to send this
File
to the api it disappears because of the stringify.... any idea how I send the image to the API along with the strings?
Sep 26, 2022, 9:25 PM
such a huge task simply to upload an image from the front-end... strings were nothing but this is v time-consuming for me so far
Sep 26, 2022, 9:26 PM
My original question in help today was "Anyone have easy examples of uploading images to sanity from Nextjs" and I still have not found a single easy example of this and I can't guess how to do it myself
Sep 26, 2022, 9:31 PM
user M
would you like me to explain again maybe clarify more what my issue is?
Sep 26, 2022, 9:42 PM
scratch this thread the code I submitted wont explain it - we can close out completely thanks for ur time
Sep 26, 2022, 9:43 PM
Regarding your most recent post in the help channel, I'm sorry you feel like support isn't happening promptly enough. I have been trying to help you to the best of my ability, but you shared your code about 20 minutes ago. Like I said, it's tough to diagnose what's wrong without seeing someone's actual code.
We get several hundred requests for help from the community each week, so unfortunately that does mean it can take a bit for us to have a chance to circle back to a thread during the day.

I do understand what your issue is, but I can't pinpoint the exact problem with your code without reading and replicating it (which again, can take some time).
Sep 26, 2022, 9:44 PM
i'm sorry - my own project is my issue not u - I just need to know how, when submitting these strings and stuff, I can also send an image file to sanity. i'm using the repo example. my most recent little git I shared w u doesn't even run it's not worth checking. ill organize it better and let ya know here.
Sep 26, 2022, 9:48 PM
Please don't take that as me saying I do not want to help you! It just means as much as I want to immediately resolve a problem, I'm unfortunately a bit limited. I am working on putting together a straightforward upload example for you!
Sep 26, 2022, 9:53 PM
are you serious? omg that would be insanely helpful...
Sep 26, 2022, 9:54 PM
problem is I have
/pages/todo.js
which has my form and which sends my strings... I dont know how to send the image file to the
/api/todo.js
call... and the in that call I dont know how to handle the file exactly.
Sep 26, 2022, 9:55 PM
So my problem is if I am uploading the image on
/pages/todo.js
but I cannot stringify it... how do I send it to the
POST
I am making with all the other stuff? Like how do I include the image file with the stringified content is my issue basically. (so sorry to rant trying to be detailed)
Sep 26, 2022, 10:05 PM
user M
if you look at this line: https://github.com/outer-code/example-4-sanity/blob/6be196b85adb5cb3b0efd426c94d2595df2dfea9/src/pages/todos.js#L60
You can see where I try to upload the image and this is where I get a permissions problem.
Sep 26, 2022, 10:34 PM
omg smacks head dude I think this was just because I didnt have NEXT_PUBLIC_ on that damn env variable hahah (sorry to swear) about to test now...
Sep 27, 2022, 12:27 AM
btw im texan we call every1 "dude".... ill lyk if this fixes it.
Sep 27, 2022, 12:27 AM
user M
think that did it... wow lol. It was an .env variable šŸ˜¬šŸ˜¬
Sep 27, 2022, 12:34 AM
Ok, so to your question about how to upload from a form in Next to Sanity. First off, I configure my client like this:
export const client = sanityClient({
  projectId: 'k8p6uw8a',
  dataset: 'development',
  apiVersion: '2021-03-25', // use current UTC date - see "specifying API version"!
  token: process.env.NEXT_PUBLIC_SANITY_WRITE_TOKEN,
});
For the token to be picked up by the browser, I have to add
NEXT_PUBLIC
to the beginning of my env. Then the component looks like this:
import React, { useState } from 'react';

import { client } from '../src/sanity';

export default function Todos({ todos }) {
  const [todoList, setTodoList] = useState(todos);
  const [userInput, setUserInput] = useState({
    text: '',
    image: '',
  });

  const handleSubmit = async e => {
    e.preventDefault();
    const { _id } = await client.assets.upload('image', userInput.image);

    const newTodo = {
      _type: 'todo',
      text: userInput.text,
      image: {
        _type: 'image',
        asset: {
          _type: 'reference',
          _ref: _id,
        },
      },
    };

    const todo = await client.create(newTodo);
    console.log(todo)
  };

  const handleChange = e => {
    e.preventDefault();
    const value = e.target.type == 'text' ? e.target.value : e.target.files[0];
    setUserInput({ ...userInput, [e.target.name]: value });
  };

  return (
    <div className='max-w-4xl mx-auto '>
      <main className='text-center'>
        <div className='my-8'>
          <h1 className='text-4xl font-bold tracking-tight '>My To-do List</h1>
        </div>
        <form>
          <div className='flex justify-center items-center'>
            <input
              name='text'
              className='w-72 h-12 border p-4 border-blue-100'
              type='text'
              value={userInput.text}
              placeholder='Make coffee.'
              // our function
              onChange={handleChange}
            />
            <div className='my-8'>
              <input type='file' onChange={handleChange} name='image' />
            </div>
          </div>{' '}
          <button
            className='focus:outline-none focus:ring focus:border-blue-800
						px-6 py-2 rounded-xl bg-blue-500 text-blue-50 hover:bg-blue-800 
						font-semibold'
            onClick={handleSubmit}
          >
            Add to do
          </button>
        </form>
        <div className='my-12'>
          <h1
            className='text-xl font-bold tracking-tight 
					my-8'
          >
            Your Todos
          </h1>
          <ul>
            {todoList &&
              todoList.map(todo => <li key={todo._id}>{todo.text}</li>)}
          </ul>
        </div>
      </main>
    </div>
  );
}

export const getServerSideProps = async () => {
  const todos = await client.fetch(`*[_type == 'todo']`);

  return {
    props: {
      todos,
    },
  };
};
This isn't the most secure way to do this though, because your token ends up showing up in the network tab. So, we can use Next API routes instead. Note, I don't need the
NEXT_PUBLIC
prefix on my env var:
/api/uploadForm.js

import { client } from '../../src/sanity';
import multer from 'multer';

async function parseFormData(req, res) {
  const storage = multer.memoryStorage();
  const multerUpload = multer({ storage });
  const multerFiles = multerUpload.any();
  await new Promise((resolve, reject) => {
    multerFiles(req, res, result => {
      if (result) {
        return reject(result);
      }
      return resolve(result);
    });
  });
  return {
    fields: req.body,
    files: req.files,
  };
}

export const config = {
  api: {
    bodyParser: false,
  },
};

export default async function handler(req, res) {
  const sanityClient = client.withConfig({
    token: process.env.SANITY_WRITE_TOKEN,
  });

  const data = await parseFormData(req, res);

  const { _id } = await sanityClient.assets.upload(
    'image',
    data.files[0].buffer
  );
  const newTodo = {
    _type: 'todo',
    text: data.fields.text,
    image: {
      _type: 'image',
      asset: {
        _type: 'reference',
        _ref: _id,
      },
    },
  };

  const todo = await sanityClient.create(newTodo);

  res.redirect('/');
}
Sep 27, 2022, 12:46 AM
In that second example, the form would look like this:
<form
          action='/api/uploadForm'
          method='POST'
          encType='multipart/form-data'
        >
          <div className='flex justify-center items-center'>
            <input
              name='text'
              className='w-72 h-12 border p-4 border-blue-100'
              type='text'
              value={userInput.text}
              placeholder='Make coffee.'
              // our function
              onChange={handleChange}
            />
            <div className='my-8'>
              <input type='file' onChange={handleChange} name='image' />
            </div>
          </div>{' '}
          <button
            className='focus:outline-none focus:ring focus:border-blue-800
						px-6 py-2 rounded-xl bg-blue-500 text-blue-50 hover:bg-blue-800 
						font-semibold'
            // onClick={handleSubmit}
          >
            Add to do
          </button>
        </form>

Sep 27, 2022, 12:47 AM
But I see that you got it sorted out now! I hope this explanation helps if you hit any other stumbling blocks!
Sep 27, 2022, 12:48 AM
VERY COOL - I will need to use elements from this to match some needs for our app next up thank you!!
Sep 27, 2022, 12:48 AM
saved these - this was so helpful
Sep 27, 2022, 12:48 AM
My method is very insecure compared to either of these so ill be refactoring w ur kindly-written code here thanks again
Sep 27, 2022, 12:49 AM
Awesome! Glad you got it working!
Sep 27, 2022, 12:51 AM

Sanityā€“ build remarkable experiences at scale

Sanity is a modern headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.

Was this answer helpful?