Ombyggnad 2026/2027 pågår - döda länkar förekommer. Mer information

Praktisk guide för att hålla dig till cURL

2026-04-18 på Anders Ytterströms webblogg

Från Just use cURL:

Q: How do I organize my requests?
A: Put your shell scripts into directories, genius.

Så låt oss göra detta med praktiska exempel! Jag gjorde detta nyligen, så jag kan gå igenom det bit för bit hur jag gjorde.

Information om API jag önskar testa:

Följande setup kommer skapas i detta inlägg:

Så här ser en typisk session ut:

. .envs/prod/sandbox
./renew-token
./saker/GET/lista-alla-saker.curl
./saker/POST/skapa-sak.curl "saken"
./saker/DELETE/radera-sak.curl "saken"
./saker/POST/skapa-sak.curl "sak2"
./saker/PUT/uppdatera-sak.curl "sak2" "saken"
./saker/DELETE/radera-sak.curl "saken"

Skapa nytt git-repo

Det går såklart att göra detta med annan backup-lösning också, men git är vad jag föredrar.

git init just-use-curl
cd just-use-curl

Jag skapar direkt en ignore-lista.

cat <<END > .gitignore
.curl-*-headers-file
END

Grundläggande filstruktur

Skapa litet filer och mappar:

mkdir {.envs/{test,pt,prod},.tests}
touch .envs/{test,pt,prod}/{sandbox,production}
touch {renew-token,set-secrets,who-am-i,init-env}
chmod +x {renew-token,set-secrets,who-am-i,init-env}
cat <<END > .gitignore

Nedbrytning om vad filerna gör:

Nedbrytning av script

Det är primärt tre script, och ett valfritt script.

init-env

Scriptet sätter upp en miljö genom att fråga efter två saker:

Detta är innehållet:

#!/bin/sh
step=1
uenv=$(echo "$1" | tr "[:lower:]" "[:upper:]")
printf "Skapar miljö - %s\n" "$uenv"
printf "[%s/2] Ange API_ENDPOINT_URL och tryck ENTER: " "$step"
read -r aeu
step=$((step + 1))
printf "[%s/2] Ange TOKEN_ENDPOINT_URL och tryck ENTER: " "$step"
read -r teu
step=$((step + 1))
mkdir -p .envs/"$1"
touch .envs/"$1"/."$1"
cat <<END > .envs/"$1"/."$1"
export TOKEN_ENDPOINT_URL=$teu
export API_ENDPOINT_URL=$aeu
END

who-am-i

Ett script som skriver ut den aktuella kontexten. Kontexten väljs genom att source:a en av env-filerna, t ex såhär:

. .envs/pt/sandbox; ./who-am-i

Detta är innehållet.

#!/bin/sh
echo "TOKEN_ENDPOINT_URL:   $TOKEN_ENDPOINT_URL"
echo "API_ENDPOINT_URL:     $API_ENDPOINT_URL"
echo "API_KEY_TYPE:         $API_KEY_TYPE"
echo "CLIENT_KEY:           $CLIENT_KEY"

renew-token

Ett POSIX-script som kollar så att vi valt en kontext, och om vi har det - så skapas och sparas en ny access token.

Detta är innehållet:

#!/bin/sh
: "${CLIENT_KEY?CLIENT_KEY saknas - aktivera en miljö?}"
: "${CLIENT_SECRET?CLIENT_SECRET saknas - aktivera en miljö?}"
: "${API_KEY_TYPE?API_KEY_TYPE saknas - aktivera en miljö?}"
: "${TOKEN_ENDPOINT_URL?TOKEN_ENDPOINT_URL saknas - aktivera en miljö?}"
: "${API_ENDPOINT_URL?API_ENDPOINT_URL saknas - aktivera en miljö?}"
CREDENTIALS=$(echo "$CLIENT_KEY:$CLIENT_SECRET" | base64)
TOKEN=$(curl -s -X POST "$TOKEN_ENDPOINT_URL" \
-d "grant_type=client_credentials" \
-H "Authorization: Basic $CREDENTIALS" | \
jq '.["access_token"]' | sed -e "s/\"//g")
cat <<END > .curl-"$CLIENT_KEY"-headers-file
User-Agent: $USER-$(curl -V | head -n1 | awk '{print $1$2;}')
Accept: application/json
Authorization: Bearer $TOKEN
END

Valfritt: set-secrets

Ett script som ger instruktioner för att klistra in consumer keys och consumer secrets per miljö.

BRa att ha, men kan göras manuellt också. Här är innehållet:

#!/bin/sh
API=$(basename "$PWD")
clear
client_credentials () {
	step=1
	echo "[$step/5] Gå till API i utvecklarportalen, och dess Subscriptions."
	echo "      Välj där korrekt Application, och använd kopieringsverktyget"
	echo "      på värdena."
	echo "      OBS! ingen inklistring av *secrets* kommer att skrivas på"
	echo "      skärmen av säkerhetsskäl."
	for env in "sandbox" "production"; do
		uenv=$(echo "$env" | tr "[:lower:]" "[:upper:]")
		step=$((step + 1))
		printf "[%s/5] Klistra in %s \"Consumer Key\" och tryck ENTER: " "$step" "$uenv"
		read -r ck
		step=$((step + 1))
		printf "[%s/5] Klistra in \"Consumer Secret\" för %s och tryck ENTER: " "$step" "$uenv"
		stty -echo
		read -r cs
		stty echo
		echo ""
		cat <<- END > ./.envs/"$1"/"$env"
		. ./.envs/$1/.$1
		export API_KEY_TYPE=$env
		export CLIENT_KEY=$ck
		export CLIENT_SECRET=$cs
		END
	done
	printf "Consumer key+secret är sparad för %s!\n" "$1"
}
echo "Sätter consumer key+secret för: $API"
for env in "test" "pt" "prod"; do
	yN=""
	while true; do
	    printf "Sätt secrets för %s i %s? [y/N]: " "$API" "$env"
	    read -r input
	    case "$input" in
		[yY])
		    yN="y"
		    break
		    ;;
		""|[nN])
		    yN="n"
		    break
		    ;;
		*)
		    printf "[!] Felaktigt svar! Ange \"y\" eller \"n\".\n"
		    ;;
	    esac
	done
	if [ "$yN" = "y" ]; then
	    client_credentials "$env"
	fi
done

Skapa miljöerna

Varje miljö (test, pt och prod) har två endpoints, sandbox och production.

Följande kommandon skapar miljöerna:

./init-env test
./init-env pt
./init-env prod

Jag vill sedan ange OAuth-keys och -secrets, det gör jag med:

./set-secrets

Verifiera så allt funkar:

. .envs/pt/sandbox
./who-am-i
./renew-token

Skapa några endpoints

För att skapa endpoints används följande filstruktur:

Exempel på hur detta kan se ut:

thing/
├── GET
│   ├── list-all.curl
│   └── list-selection.curl
└── POST
    ├── append-new.error.curl
    └── append-new.ok.curl

Endpoint som bara hämtar data (GET, OPTIONS, HEAD)

Behöver inga argument.

Exempel på hur det kan se ut:

#!/bin/sh
res="thing"
curl -s \
-H @.curl-"$CLIENT_KEY"-headers-file \
"$API_ENDPOINT_URL"/"$res" \
| python3 -m json.tool

Standardverbet i cURL är GET. Använd -X [OPTIONS, HEAD] för att ändra verbet.

(I exemplet här är det ett JSON-API, så Pythons inbyggda json.tool används för att snygga till utskriften)

Endpoints som behöver indata (t ex POST, PUT, PATCH)

Behöver argument, t ex via stdin eller som variabel i filen.

Exempel på hur det kan se ut:

#!/bin/sh
res="thing"

# definiera data i filen
# exempel: ./thing/POST/new.curl 
data="code=success&payload=allgood"
resp=$(curl -s -w "%{http_code}\n" \
-H @.curl-"$CLIENT_KEY"-headers-file \
-d "$data" "$API_ENDPOINT_URL"/"$res")

# skicka in stdin som payload
# exempel: cat payload.txt | ./thing/POST/new.curl 
resp=$(curl -s -w "%{http_code}\n" \
-H @.curl-"$CLIENT_KEY"-headers-file \
-d "$(cat -)" "$API_ENDPOINT_URL"/"$res")

# skicka in första argumentet som data
# exempel: ./thing/POST/new.curl "foo=1" 
resp=$(curl -s -w "%{http_code}\n" \
-H @.curl-"$CLIENT_KEY"-headers-file \
-d "$1" "$API_ENDPOINT_URL"/"$res")

# skicka in en sträng som data och placera den i nyckeln "payload"
# exempel: ./thing/POST/new.curl hej 
resp=$(curl -s -w "%{http_code}\n" \
-H @.curl-"$CLIENT_KEY"-headers-file \
-d "payload=$1" "$API_ENDPOINT_URL"/"$res")

# Skriv ut HTTP-statuskod från svar om ingen övrig data skickades med,
# och formattera JSON om sådan finns
if [ "$(echo "$resp" | wc -w)" -eq 1 ]
then
	echo "HTTP $resp"
else
	echo "$resp" | tr -d '\n' | sed -r 's/[0-9]+$//' | python3 -m json.tool
fi

Standardverbet när data skickas (curl -d) är POST. Använd -X [DELETE, PATCH, PUT] för att ändra verbet.

(I exemplet här är det ett JSON-API, så Pythons inbyggda json.tool används för att snygga till utskriften)

Endpoints som behöver ange ett ID eller referens (GET, DELETE, PUT, PATCH)

#!/bin/sh
res="thing"

# Ange id/referens med första argumentet,
# exempel: ./thing/DELETE/delete-single.curl 23 
curl -X DELETE -s -w "HTTP %{http_code}\n" \
-H @.curl-"$CLIENT_KEY"-headers-file \
"$API_ENDPOINT_URL"/"$res"/"$1"

# Ange id/referens med stdin
# exempel: echo 23 | ./thing/DELETE/delete-single.curl 
curl -X DELETE -s -w "HTTP %{http_code}\n" \
-H @.curl-"$CLIENT_KEY"-headers-file \
"$API_ENDPOINT_URL"/"$res"/"$(cat -)"

Använd -X [DELETE, PATCH, PUT] för att ändra verbet, eller skippa -X för att falla tillbaka på GET. Kika på tidigare exempel på hur koden kan utökas med en if-sats som skriver ut statuskod eller paylaod beroende på svar.

(I exemplet här är det ett JSON-API, så Pythons inbyggda json.tool används för att snygga till utskriften)

Mer avancerade endpoints

Det finns några exempelscripts jag utelämnat här:

Jag ger det som en övning att själv lista ut. :)

Skala över tid

Först av allt - versionshantering med git är en bra start, och kan nog räcka med en bra tillämpning av taggar och branches.

Jag rekommenderar dock att låta filstrukturen spegla alla versioner av API som ligger i produktion, då det inte ovanligt att flera gamla och nya versioner samexisterar, te x på grund av krav på bakåtkompabilitet eller migreringsperiod.

Jag hade därför gjort något liknande över tid:

apis
├── README
├── demo-api
│   └── v2
│   │   ├── .envs
│   │   ├── .tests
│   │   ├── ...
│   │   ├── init-env
│   │   ├── README
│   │   ├── renew-token
│   │   ├── set-secrets
│   │   └── who-am-i
│   └── v3
│       ├── .envs
│       ├── .tests
│       ├── ...
│       ├── init-env
│       ├── README
│       ├── renew-token
│       ├── set-secrets
│       └── who-am-i
└── another-api
    └── v1.0.0
        ├── .envs
        ├── .tests
        ├── ...
        ├── init-env
        ├── README
        ├── renew-token
        ├── set-secrets
        └── who-am-i

Integrationstester

Den absolut främsta styrkan med att ha en sådan här uppsättning cURL-scripts är att integrations- och e2e-testning blir busenkelt.

Jag vill ha dessa scripts i katalogen .tests.

Hamra API

För exempel, detta script genererar nya API-anrop så länge det är aktivt. Användbart vid konfiguration av loggar/traces eller liknande.

#!/bin/sh
./renew-token
while :; do
	./a/POST/add-word.curl anarkiz
	./b/POST/add-word.curl baersaerkz
	./c/POST/add-word.curl cesarstoetz
	sleep 3
	./a/PUT/replace-word.curl anarkiz ansjovisz
	./b/PUT/replace-word.curl baersaerkz baenkpressz
	./c/PUT/replace-word.curl cesarstoetz chockz
	sleep 3
	./a/DELETE/delete-word.curl ansjovisz
	./b/DELETE/delete-word.curl baenkpressz
	./c/DELETE/delete-word.curl chockz
	sleep 3
done

Heartbeat-test

Jag gillar att göra ett sk heartbeat-test i samband med att tjänsten uppdaterats eller startats om. Det kan se ut såhär.

#!/bin/sh
salt=$(date +"%s")
echo "\e[0;36;1m[INFO]\e[0m Valt API: \e[0;93;1mABC Demo API, v1.0.0\e[0m"
echo "\e[0;36;1m[INFO]\e[0m Miljöer: \e[0;93;1mtest, prodtest, produktion\e[0m"
s=1
for env in "test" "pt" "prod"; do
        for ep in "production"; do
                uenv=$(echo "$env" | tr "[:lower:]" "[:upper:]")
                printf "\e[0;36;1m["$s"/3]\e[0m %s\n" "$uenv"
                if ! . .envs/"$env"/"$ep" > /dev/null; then
                        echo "NOOP - miljön är inte konfigurerad"
                        continue
                fi
                sleep 1
                if ./renew-token > /dev/null; then
                        echo "\e[0;32;1mOK\e[0m - Kan authentisera"
                else
                        echo "\e[0;31;1mERROR\e[0m - autentisering trasig!"
                        continue
                fi
                sleep 1
                if ./a/GET/list-all-words.curl > /dev/null; then
                        echo "\e[0;32;1mOK\e[0m - API svarar på anrop"
                else
                        echo "\e[0;31;1mERROR\e[0m - API svarar inte"
                fi
                sleep 1
                s=$((s+1))
        done
done

Ovanstående inlägg publicerades 2026-04-18. Det går att prenumerera på denna webblogg. Härifrån är följande mål rekommenderade: