Solved

Returns Error 405 on embedded Node.js app

jojo_
Tourist
9 2 1

As said in my earlier post , somehow i managed to learn about the REST API and you can only create a SHOP metafield (Storefront) using GraphQL API. 

 

Now i'm trying to make a post request for a SHOP metafield. But it results in 405 method not allowed error. I searched the forums and found no clue about this. If anyone is willing to help, much appreciated. 

 

    axios({
      method: "post",
      url: "/admin/api/2020-04/metafields.json",
      data: {
        metafield: {
          namespace: "shop",
          key: "discount",
          value: discount,
          value_type: "string",
        },
      },
      headers: {
        "Content-Type": "application/json",
        "X-Shopify-Access-Token": accessToken,
      },
    });

 Here is the entire code of annotated-layout.js if that helps to understand the issue

 

import {
  Button,
  Card,
  Form,
  FormLayout,
  Layout,
  Page,
  SettingToggle,
  Stack,
  TextField,
  TextStyle,
} from "@shopify/polaris";
import axios from "axios";

class AnnotatedLayout extends React.Component {
  state = {
    discount: "10%",
    enabled: false,
  };

  render() {
    const { discount, enabled } = this.state;
    const contentStatus = enabled ? "Disable" : "Enable";
    const textStatus = enabled ? "enabled" : "disabled";

    return (
      <Page>
        <Layout>
          <Layout.AnnotatedSection
            title="Default discount"
            description="Add a product to Sample App, it will automatically be discounted."
          >
            <Card sectioned>
              <Form onSubmit={this.handleSubmit}>
                <FormLayout>
                  <TextField
                    value={discount}
                    onChange={this.handleChange("discount")}
                    label="Discount percentage"
                    type="discount"
                  />
                  <Stack distribution="trailing">
                    <Button primary submit>
                      Save
                    </Button>
                  </Stack>
                </FormLayout>
              </Form>
            </Card>
          </Layout.AnnotatedSection>
          <Layout.AnnotatedSection
            title="Price updates"
            description="Temporarily disable all Sample App price updates"
          >
            <SettingToggle
              action={{
                content: contentStatus,
                onAction: this.handleToggle,
              }}
              enabled={enabled}
            >
              This setting is{" "}
              <TextStyle variation="strong">{textStatus}</TextStyle>.
            </SettingToggle>
          </Layout.AnnotatedSection>
        </Layout>
      </Page>
    );
  }

  handleSubmit = () => {
    this.setState({
      discount: this.state.discount,
    });

    axios({
      method: "post",
      url: "/admin/api/2020-04/metafields.json",
      data: {
        metafield: {
          namespace: "shop",
          key: "discount",
          value: "10%",
          value_type: "string",
        },
      },
      headers: {
        "Content-Type": "application/json",
        "X-Shopify-Access-Token": xxxxxxxxxxxxxxxxx,
      },
    });

    console.log("submission", this.state);
  };

  handleChange = (field) => {
    return (value) => this.setState({ [field]: value });
  };

  handleToggle = () => {
    this.setState(({ enabled }) => {
      return { enabled: !enabled };
    });
  };
}

export default AnnotatedLayout;

 

 

Accepted Solution (1)
jojo_
Tourist
9 2 1

This is an accepted solution.

Okay, I finally figured it out. I was trying to do a post from client side and not from server side.  This post has been very helpful.

Also I was duplicating the router.get() for accepting the POST from client side.  I found this stackoverflow answer and update the code as below. Now its responding to axios post ( 200 ) and next thing would be adding an api call from server.js file. 

 

axios.post("/metafield", 
      {"metafield":{"namespace":"shop","key":"discount","value":"30%","value_type":"string"}},
      options)
      .then((response) => {
        console.log(response);
      }, (error) => {
        console.log(error);
      });
              
     
  router.post('/metafield', verifyRequest(), async ctx => {
    ctx.body = ctx.request.body;
    ctx.res.statusCode = 200;
});

@chipkeyes88 @SBD_  Thanks for your time.

 

 

View solution in original post

Replies 5 (5)

SBD_
Shopify Staff
1829 269 405

Hey @jojo_ 

 

405 indicates the wrong method. From first glance that code looks OK. Are you able to capture a request ID from the response headers?

Scott | Developer Advocate @ Shopify 

jojo_
Tourist
9 2 1

@SBD_  Thanks for looking into this.  I fixed the 405 error by adding a post method to router.

router.post("*", verifyRequest(), async ctx => {
    await handle(ctx.req, ctx.res);
    ctx.respond = false;
    ctx.res.statusCode = 200;
  });

But now its showing a new error Uncaught (in promise) Error: Request failed with status code 501

 

Initially i thought it is related to access scopes, but figured out there isn't any specific access code for shop resource. https://community.shopify.com/c/Shopify-APIs-SDKs/Which-scope-do-I-need-to-access-shop-metafields/td...

 

This is my latest code

let jsonData = JSON.stringify(
      {
        "metafield": {
          "namespace": "shop",
          "key": "discount",
          "value": "30%",
          "value_type": "string"
        }
      }
    );
    axios({
      method: "post",
      url: "/admin/api/2020-04/metafields.json",
      data: jsonData,
      headers: {
        "Content-Type": "application/json",
        "X-Shopify-Access-Token": accessToken,
      },
    });

 

And then I tried the same with Postman.  POST for the same value results in below error.

metafield: required parameter missing or invalid.

 

Thanks again!

 

 

 

 

 

 

 

 

 

chipkeyes88
Shopify Partner
14 3 13

Have you tried without calling JSON.stringify on the data payload. I think axios will work fine with straight JSON and the stringify may actually be throwing off how the body is sent in the request. Haven't tested but just a thought.

jojo_
Tourist
9 2 1

Still getting the same error 501 @chipkeyes88 

 

method=POST path="/admin/api/2020-04/metafields.json" host=demoo-app-new.herokuapp.com request_id=1999a2f8-c959-4d56-9113-46aee1a1669b fwd="111.92.74.226" dyno=web.1 connect=1ms service=168ms status=501 bytes=223 protocol=https
    const options = {
      headers : {"X-Shopify-Access-Token": accessToken, "Content-Type": "application/json"}
    };

    axios.post("/admin/api/2020-04/metafields.json", 
              {"metafield":{"namespace":"shop","key":"discount","value":"30%","value_type":"string"}},
              options);
     
jojo_
Tourist
9 2 1

This is an accepted solution.

Okay, I finally figured it out. I was trying to do a post from client side and not from server side.  This post has been very helpful.

Also I was duplicating the router.get() for accepting the POST from client side.  I found this stackoverflow answer and update the code as below. Now its responding to axios post ( 200 ) and next thing would be adding an api call from server.js file. 

 

axios.post("/metafield", 
      {"metafield":{"namespace":"shop","key":"discount","value":"30%","value_type":"string"}},
      options)
      .then((response) => {
        console.log(response);
      }, (error) => {
        console.log(error);
      });
              
     
  router.post('/metafield', verifyRequest(), async ctx => {
    ctx.body = ctx.request.body;
    ctx.res.statusCode = 200;
});

@chipkeyes88 @SBD_  Thanks for your time.